package com.itheima.stock.service.impl;

import com.alibaba.excel.EasyExcel;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.itheima.stock.mapper.StockMarketIndexInfoMapper;
import com.itheima.stock.mapper.StockRtInfoMapper;
import com.itheima.stock.pojo.domain.InnerMarketDomain;
import com.itheima.stock.pojo.domain.Stock4EvrDayDomain;
import com.itheima.stock.pojo.domain.Stock4MinuteDomain;
import com.itheima.stock.pojo.domain.StockUpdownDomain;
import com.itheima.stock.pojo.vo.StockInfoConfig;
import com.itheima.stock.service.StockService;
import com.itheima.stock.utils.DateTimeUtil;
import com.itheima.stock.vo.resp.PageResult;
import com.itheima.stock.vo.resp.R;
import com.itheima.stock.vo.resp.ResponseCode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.*;

@Service
@Slf4j //lombok提供的日志记录,在哪个类上添加此注解,就可以在那个类中进行使用
public class StockServiceImpl implements StockService {
    // 大盘mapper对象
    @Autowired
    private StockMarketIndexInfoMapper stockMarketIndexInfoMapper;

    // 个股mapper对象
    @Autowired
    private StockRtInfoMapper stockRtInfoMapper;

    // 存放大盘标识变量
    @Autowired
    private StockInfoConfig stockInfoConfig;

    @Autowired
    private RedisTemplate redisTemplate;


    @Override
    public R<List<Stock4EvrDayDomain>> getDayKLinData(String code) {
        //1.获取查询的日期范围
        //1.1 获取截止时间
        DateTime endDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        Date endTime = endDateTime.toDate();
        //TODO MOCKDATA
        endTime=DateTime.parse("2022-01-07 15:00:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        //1.2 获取开始时间
        DateTime startDateTime = endDateTime.minusDays(10);
        Date startTime = startDateTime.toDate();
        //TODO MOCKDATA
        startTime=DateTime.parse("2022-01-01 09:30:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        //2.调用mapper层进行查询
        List<Stock4EvrDayDomain> list = stockRtInfoMapper.getStockInfo4EvrDay(code,startTime,endTime);
        return R.ok(list);
    }

    /**
     * 查询个股分时图
     * @param code
     * @return
     */
    @Override
    public R<List<Stock4MinuteDomain>> stockScreenTimeSharing(String code) {
        //1.获取最近最新的交易时间点和对应的开盘日期
        //1.1 获取最近有效时间点
        DateTime lastDate4Stock = DateTimeUtil.getLastDate4Stock(DateTime.now());
        Date endTime = lastDate4Stock.toDate();
        //TODO mockdata
        endTime=DateTime.parse("2021-12-30 14:47:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();

        //1.2 获取最近有效时间点对应的开盘日期
        DateTime openDateTime = DateTimeUtil.getOpenDate(lastDate4Stock);
        Date startTime = openDateTime.toDate();
        //TODO MOCK DATA
        startTime=DateTime.parse("2021-12-30 09:30:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        //2.查询个股分时图数据
        List<Stock4MinuteDomain> list = stockRtInfoMapper.getStockInfoByCodeAndDate(code,startTime,endTime);
        return R.ok(list);
    }

    /**
     * 个股涨幅区间统计
     * @return
     */
    @Override
    public R<Map> getStockUpDown() {
        //1.获取股票最新一次交易的时间点
        Date curDate = DateTimeUtil.getLastDate4Stock(DateTime.now()).toDate();
        //mock data
        curDate=DateTime.parse("2022-01-06 09:55:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        //2.查询股票信息
        List<Map> maps=stockRtInfoMapper.getStockUpDownSectionByTime(curDate);
        //3.封装响应结果数据
        HashMap<Object, Object> map = new HashMap<>();
        // todo: ===================将查询结果序列化处理===============
        // 获取有序的标记
        List<String> upDownRange = stockInfoConfig.getUpDownRange();
        // 存放有序的结果信息
        ArrayList<Map> orderMaps = new ArrayList<>();
        // 遍历有序的标记      "<-7%" "-7~-5%" "-5~-3%" "-3~0%" "0~3%" "3~5%" "5~7%" ">7%"
        for (String rangeStr : upDownRange) {
            // 定义Map集合
            Map m = null;
            for (Map map1 : maps) {
                String title = (String) map1.get("title");
                if (rangeStr.equals(title)){
                    m = map1;
                }
            }
            // 判断m是否为null,如果为空说明在此区间内没有数据信息
            if (m!=null){
                orderMaps.add(m);
            }else{
                m = new HashMap();
                m.put("count",0);
                m.put("title",rangeStr);
                orderMaps.add(m);
            }
        }


        // todo: ===================将查询结果序列化处理===============
        // 将当前时间格式化成字符串
        String curDateStr = new DateTime(curDate).toString(DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        map.put("time",curDateStr);
        map.put("infos",orderMaps);
        return R.ok(map);
    }

    /**
     * 两市成交量对比功能
     * @return
     */
    @Override
    public R<Map> stockTradeVol4InnerMarket() {
        // 获取大盘标识
        List<String> inners = stockInfoConfig.getInner();
        // 推算T日的当前时间和开盘时间
        DateTime lastDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        DateTime openDateTime = DateTimeUtil.getOpenDate(lastDateTime);
        // 转成java的日期格式
        Date startTime4T = openDateTime.toDate();
        Date endTime4T=lastDateTime.toDate();
        // todo: mock数据
        startTime4T=DateTime.parse("2022-01-03 09:30:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        endTime4T=DateTime.parse("2022-01-03 14:40:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();

        // 推算T-1日的当前时间和开盘时间
        DateTime preLastDateTime = DateTimeUtil.getPreviousTradingDay(lastDateTime);
        DateTime preOpenDateTime = DateTimeUtil.getOpenDate(preLastDateTime);
        // 转成java的日期格式
        Date startTime4PreT = preOpenDateTime.toDate();
        Date endTime4PreT=preLastDateTime.toDate();
        //TODO: mock数据
        startTime4PreT=DateTime.parse("2022-01-02 09:30:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        endTime4PreT=DateTime.parse("2022-01-02 14:40:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        // 查询T日的成交量数据
        List<Map> tData = stockMarketIndexInfoMapper.getStockTradeVol(inners,startTime4T,endTime4T);
        // 查询T-1日的成交量数据
        List<Map> pretData = stockMarketIndexInfoMapper.getStockTradeVol(inners,startTime4PreT,endTime4PreT);
        // 封装响应结果
        HashMap<String, Object> map = new HashMap<>();
        map.put("amtList",tData);
        map.put("yesAmtList",pretData);
        return R.ok(map);
    }

    /**
     * 个股数据导出
     * @param response
     * @param page
     * @param pageSize
     */
    @Override
    public void stockExport(HttpServletResponse response, Integer page, Integer pageSize) {
        try {
            //1.获取最近最新的一次股票有效交易时间点（精确分钟）
            Date curDate = DateTimeUtil.getLastDate4Stock(DateTime.now()).toDate();
            //因为对于当前来说，我们没有实现股票信息实时采集的功能，所以最新时间点下的数据
            //在数据库中是没有的，所以，先临时指定一个假数据,后续注释掉该代码即可
            curDate=DateTime.parse("2022-01-05 09:47:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
            //2.设置分页参数 底层会拦截mybatis发送的sql，并动态追加limit语句实现分页
            PageHelper.startPage(page,pageSize);
            //3.查询
            List<StockUpdownDomain> infos=stockRtInfoMapper.getNewestStockInfo(curDate);
            //如果集合为空，响应错误提示信息
            if (CollectionUtils.isEmpty(infos)) {
                //响应提示信息
                R<Object> r = R.error(ResponseCode.NO_RESPONSE_DATA);
                response.setContentType("application/json");
                response.setCharacterEncoding("utf-8");
                // 将R对象转成json
                String rjson = new ObjectMapper().writeValueAsString(r);
                response.getWriter().write(rjson);
                return;
            }
            /**
             * 文件下载:
             *      2个响应头,一个输出流
             *      第一个响应头: 告诉浏览器本次响应的是一个什么格式的文件
             *          ContentType类型: 设置响应文件的类型
             *      第二个响应头: 文件下载专用头,告诉浏览器本次响应的结果数据,需要单独保存,而不是在浏览器上直接展示
             *          content-disposition="attachment;filename=xxx.xlsx"
             *      一个输出流:
             *          将文件以流的形式写回给浏览器,让浏览器进行保存
             */
            //设置响应excel文件格式类型
            response.setContentType("application/vnd.ms-excel");
            //2.设置响应数据的编码格式
            response.setCharacterEncoding("utf-8");
            //3.设置默认的文件名称
            // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
            String fileName = URLEncoder.encode("stockRt", "UTF-8");
            //设置默认文件名称：兼容一些特殊浏览器
            response.setHeader("content-disposition", "attachment;filename=" + fileName + ".xlsx");
            //4.响应excel流
            EasyExcel
                    .write(response.getOutputStream(),StockUpdownDomain.class)
                    .sheet("股票信息")
                    .doWrite(infos);
        } catch (IOException e) {
            e.printStackTrace();
            log.info("当前导出数据异常，当前页：{},每页大小：{},异常信息：{}",page,pageSize,e.getMessage());
        }

    }

    /**
     * 统计最新交易日下股票每分钟涨跌停的数量
     * @return
     */
    @Override
    public R<Map> getStockUpdownCount() {
        //1.获取最新的交易时间范围 openTime  curTime
        //1.1 获取最新股票交易时间点
        DateTime curDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        Date curTime = curDateTime.toDate();
        //TODO: mock数据,当前时间(最后的有效交易时间点)
        curTime= DateTime.parse("2022-01-06 14:25:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        //1.2 获取最新交易时间对应的开盘时间
        DateTime openDate = DateTimeUtil.getOpenDate(curDateTime);
        Date openTime = openDate.toDate();
        //TODO: mock数据,与当前有效交易时间对应的开盘时间
        openTime= DateTime.parse("2022-01-06 09:30:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        //2.查询涨停数据
        //约定mapper中flag入参： 1-> 涨停数据 0-> 跌停
        List<Map> upCounts=stockRtInfoMapper.getStockUpdownCount(openTime,curTime,1);
        //3.查询跌停数据
        List<Map> dwCounts=stockRtInfoMapper.getStockUpdownCount(openTime,curTime,0);
        //4.封装响应结果
        HashMap<String, Object> map = new HashMap<>();
        // 封装涨停数据
        map.put("upList",upCounts);
        // 封装跌停数据
        map.put("downList",dwCounts);
        return R.ok(map);
    }

    /**
     * 更多个股涨幅统计
     * @param page
     * @param pageSize
     * @return
     */
    @Override
    public R<PageResult> getStockPageInfo(Integer page, Integer pageSize) {
        // PageHelper插件
        // 1.将page和pageSize两个值绑定到当前线程上(底层使用了ThreadLocal)
        PageHelper.startPage(page,pageSize);
        // 2.动态的获取股票有效交易时间
        Date curDate = DateTimeUtil.getLastDate4Stock(DateTime.now()).toDate();
        //todo: mock数据,模拟数据信息
        curDate= DateTime.parse("2022-06-07 15:00:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        // 3.执行查询
        List<StockUpdownDomain> list = stockRtInfoMapper.getNewestStockInfo(curDate);
        if (list==null || list.size()==0){
            return R.error(ResponseCode.NO_RESPONSE_DATA);
        }
        // 4.获取pageHelper的查询结果数据
        PageInfo<StockUpdownDomain> pageInfo = new PageInfo<>(list);
        // 封装返回结果
        PageResult<StockUpdownDomain> pageResult = new PageResult<>(pageInfo);
        return R.ok(pageResult);
    }

    @Override
    public R<List<InnerMarketDomain>> innerIndexAll() {
        // todo: 先从redis中获取数据
        List<InnerMarketDomain> datas = redisTemplate.opsForList().range("innerMarketInfos", 0, -1);
        //正常逻辑下redis缓存中必然存在数据
        if (!CollectionUtils.isEmpty(datas)) {
            return R.ok(datas);
        }
        //1.查询哪些大盘数据
        List<String> inners = stockInfoConfig.getInner();
        System.out.println(inners);
        //2.查询哪个时间点
        Date lastDate = DateTimeUtil.getLastDate4Stock(DateTime.now()).toDate();
        // mock数据: 做假数据
        //TODO mock测试数据，后期数据通过第三方接口动态获取实时数据 可删除
        lastDate=DateTime.parse("2021-12-28 09:31:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        //3.查询大盘数据信息
        List<InnerMarketDomain> list = stockMarketIndexInfoMapper.getMarketInfo(inners,lastDate);
        if (list==null || list.size()==0){
            return R.error(ResponseCode.NO_RESPONSE_DATA.getMessage());
        }
        // 将查询到的数据存放到redis中
        redisTemplate.opsForList().leftPushAll("innerMarketInfos",list);
        //4.封装返回结果
        return R.ok(list);
    }
}
